#include <linux/init.h>         //包含宏定义得头文件
#include <linux/module.h>       //包含初始化加载模块得头文件
#include <linux/kdev_t.h>       //处理设备号宏定义的头文件
#include <linux/fs.h>           //文件操作集
#include <linux/uaccess.h>      //copy_to_user函数的文件
#include <linux/io.h>           //ioermap等函数的头文件
#include <linux/platform_device.h>       //自动创建设备节点的头文件
#include <linux/ioport.h>
// #include <linux/cdevdevice.h>   //杂项设备头文件
/*包含头文件*/
/******************platfrom平台总线模型platform_driver文件prode函数的编写**********************
*平台总线模型的目的是将驱动代码和硬件资源的描述分隔开从而提高驱动代码的复用性因此分开了驱动 总线 设备
三个文件来描述，device driver
*（1）设计平台总线模型需要分别注册device与driver，两者通过字符串命名来实现配对
*（2）先定义一个platform_driver结构体变量实现各个结构体成员
*（3）命名一样配对device与driver后完成probe函数的编写，主要重点完成probe函数的编写
*（4）编写probe函数的思路：从device获取资源、注册杂项\字符设备，完善file_ops，并生成设备节点
*（5）$完善的probe目的是获取到了硬件资源，随后就需要完成杂项设备或字符设备的注册从而控制硬件资源
*（6）$注册杂项设备完善file_ops结构体生成设备节前需要先登记request_mem_region(start,n,name)登记到
内核告诉内核需要使用寄存器
*（7）$注册杂项设备或者字符设备
* (8)注意点：
***********************************************************************/
/*宏定义*/


/*全局变量*/
struct resource *beep_mem;//存储platform_get_resource函数的返回值
struct resource *beep_mem_tmp;
int*vir_gpio5_dr;

/*文件操作集的结构体*/
int cdev_open(struct inode*inode,struct file * file)
{
    printk("hello cdev opnen\n");
    return 0;
}

int cdev_release(struct inode*inode,struct file * file)
{
    printk("hello cdev cdev_release byb byb!\n");
    return 0;
}

int cdev_read(struct file * file,char _user * ubuf,size_t size,loff_t*loff_t)
{
    char kbuf[64]="hello";
    if(copy_to_user(ubuf,kbuf,strlen(kbuf)!=0))//拷贝信息到应用层
    {
        printk("copy_to_user error!\n");
        return -1;
    }

    printk("hello cdev_read file!\n");
    return 0;
}

//获取到应用层的数据到内核层在改函数进行io的控制操作
int cdev_write(struct file * file,char _user * ubuf,size_t size,loff_t*loff_t)
{
     char kbuf[64]={0};
    if(copy_form_user(kbuf,ubuf,size)!=0)//从应用层拷贝数据到内核层
    {
         printk("copy_form_user error!\n");
        return -1;
    }
    printk("copy_form_user %s!\n",kbuf);
    if(kbuf[0]==1)
        vir_gpio5_dr=   
    return 0;
}

struct file_operations cdevdev_fops={
    .owner = THIS_MODULE,
    .open  = cdev_open,
    .release = cdev_release,
    .read = cdev_read,
    .write = cdev_write
};

/*描述杂项设备的结构体*/
struct miscdevice misc_dev={
.minor = MISC_DYNAMIC_MINOR, //
.name  ="hello_misc",       //设备节点的名称查看dev
.fops  =&misc_fops1
};

/*************platform_driver内容处理************/
/***编写probe函数方法**
*方法一：直接获取
*方法二：使用函数获取resource * platform_get_resource()
*probe函数是device匹配成功后就会运行的函数因此在获取到硬件资源后，
*对设备的注册也是在该函数中完成，可以使用杂项设备也可以使用字符设
*********************/
int beep_probe(struct platform_device*pdev)
{
    int reg;
    printk("beep_probe\n");
    /*方法一:直接获取*/
    //printk("beep_res is %s\n",pdev->resource[0].name);//直接访问device文件中的变量
    /*方法二:使用函数获取resource * platform_get_resource()*/
    beep_mem=platform_get_resource(pdev,IORESOURCE_MUN,0);//获取到device文件中初始化硬件资源的数组，0代表的是数组的首位变量
    if(beep_mem==NULL){
        printk("platform_get_resource error!\n");
        return -EBUSY;
    }
    printk("platform_get_resource ok!\n");
    printk("device_name is:%s\n",beep_mem->name);
#if 0
    beep_mem_tmp=request_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep_test");//登记到内核，告诉内核需要用到这个寄存器
    if(beep_mem_tmp==NULL){
        printk("platform_get_resource error!\n");
        goto err_region;//登记失败需要释放登记
    }
#endif
/****************************************************/
    vir_gpio5_dr=ioermap(beep_mem->start,4);
    if(vir_gpio5_dr==NULL)
    {
        printk("GPIO5_DR ioermap error\n");
        return -EBUSY;
    }
    printk("GPIO5_DR ioermap ok!\n");
/***************************************************/
 ret = misc_register(&misc_dev);//注册杂项设备，自动生成设备节点
    if(ret<0)
    {
        printk("misc register is error\n");
        return -1;
    }
    printk("misc register is ok\n");
    return 0;

err_region:
    release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);
     return -EBUSY;
}

/***编写beep_remove函数方法***

*********************/
int beep_remove(struct platform_device*pdev)
{
    printk("beep_remove byb!\n");
    return 0;
}

const struct platform_device_id beep_idtable={
    .name="beep_test"//优先级别比driver.name要高
};

struct platform_driver beep_driver={   //通过.name名字与device匹配成功后会执行prode函数
    .probe =beep_probe, //prode函数platform主要的函数
    .remove=beep_remove,
    .driver={
        .owner = THIS_MODULE,
        .name="beep_test"
    },
    .id_table = &beep_idtable   //该变量比.driver中的.name优先级要高
};

// static cdev cdev_dev ={
//     cdev_dev.owner=THIS_MODULE, //原型struct module *owner指的是内核模块的对象指针名称
//     cdev_dev.ops  =&cdevdev_fops
// }
/*功能实现*/
static int beep_driver_init(void)
{
   
    int plat_reg=0;
    plat_reg =platform_driver_register(&beep_driver);
    if(plat_reg<0){
        printk("platform_driver_register error!\n");
    }
    printk("platform_driver_register ok!\n");

    return 0;
}

static void beep_driver_exit(void)//退出函数没有返回值
{
    platform_driver_unregister(&beep_driver);
    misc_deregister(&misc_dev);
    iounmap(vir_gpio5_dr);
    printk("byb byb !");  
}     


module_init(beep_driver_init);
module_exit(beep_driver_exit); 
/*驱动的出口与入口函数都在此处*/
MODULE_LICENSE("GPL");
/*申明信息*/
	
/*注意在开发板中加载驱动的时候一定要保持板卡的内核和编译的内核是一致的否则会报错
*查看设备节点：ls /dev/hello_cdev
*/

